<script setup>
import { onMounted, ref, toRef, getCurrentInstance, reactive } from "vue"
import * as TT from "/@/libs/threejs-tools/src"

/**
 * init websocket
 */
const wsRecvMsg = ref("")
;(() => {
  const { proxy } = getCurrentInstance()
  proxy.wsWrapper.getEmitter().on("ws-message", (msg) => {
    console.log("wsEmitter.on:ws-message", msg)
    wsRecvMsg.value = msg
  })
})()

onMounted(() => {
  initThreejs()
})

/**
 * init threejs and load stl model
 */
const container2d = ref(null)
const containerWire = ref(null)
const container = ref(null)

function initThreejs() {
  TT.initDefault(container.value, container, {
    cameraPosition: [0, 0, 500],
    clearColor: "#333",
  })
  TT.initDefault(containerWire.value, containerWire, {
    cameraPosition: [0, 0, 500],
    clearColor: "#333",
  })
  TT.initDefault(container2d.value, container2d, {
    cameraPosition: [0, 0, 500],
    clearColor: "#333",
  })
  TT.loadStl("/model/Monarch_XI_branded.stl").then((res) => {
    container.mesh = res
    container.scene.add(container.mesh)
    TT.fitView(container, container.mesh)

    containerWire.mesh = container.mesh.clone()
    containerWire.mesh.material = container.mesh.material.clone()
    containerWire.mesh.material.wireframe = true
    containerWire.scene.add(containerWire.mesh)
    TT.fitView(containerWire, containerWire.mesh)
  })
}

function onGen2DViewClick() {
  const [ctx, mesh] = gen2DView()
  const gridConfig = addGrid(ctx, mesh)
  const cellIndexMap = statCell(gridConfig, mesh)
  showCell(ctx, gridConfig, cellIndexMap)
}

/**
 * gen 2d view
 */
function gen2DView() {
  // clone mesh
  if (container2d.mesh) {
    container2d.scene.remove(container2d.mesh)
  }
  container2d.mesh = container.mesh.clone()
  // container2d.mesh.material.wireframe = true
  container2d.scene.add(container2d.mesh)
  container2d.camera.position.set(...container.camera.position)

  // transform position
  // 严格来说，是要找出模型的正面法向量，再计算出法向量对应的投影矩阵，将position.array的每一个点的坐标向量与投影矩阵相乘，或将模型旋转到正面法向量与z轴重合
  // 这里模型正面基本是对着z轴正方向，所以先简单处理，将z坐标设置为0，得到投影坐标
  const position = container2d.mesh.geometry.attributes.position
  for (let i = 0; i < position.count; i++) {
    position.array[i * position.itemSize + 2] = 0
  }

  return [container2d, container2d.mesh]
}

/**
 * add grid helper for mesh
 */
function addGrid(ctx, mesh) {
  const { center, radius } = TT.getBoundingSphere(mesh)
  const gridConfig = {
    size: 2.2 * radius,
    divisions: 22,
    center,
  }
  ctx.grid && ctx.scene.remove(ctx.grid)
  ctx.grid = TT.addGrid(ctx, gridConfig)
  ctx.grid.rotation.x = Math.PI * 0.5
  ctx.grid.position.set(center.x, center.y, center.z)
  TT.fitView(ctx, mesh)

  return gridConfig
}

/**
 * statistic the grid cell occupied by 2d view
 */
function getIntArrBetweenRange(start, end) {
  let startInt = Math.ceil(Math.min(start, end))
  let endInt = Math.floor(Math.max(start, end))
  return new Array(endInt - startInt + 1).fill(0).map((_, i) => startInt + i)
}

function statCell(gridConfig, mesh) {
  const cellIndexMap = {}

  const position = mesh.geometry.attributes.position
  const cellSize = gridConfig.size / gridConfig.divisions
  const [gridLeft, gridBottom] = [
    gridConfig.center.x - gridConfig.size / 2,
    gridConfig.center.y - gridConfig.size / 2,
  ]
  const posArr = position.array
  for (let i = 1; i < position.count; i++) {
    if (i % 3 === 0) {
      continue
    }
    const [lineStart, lineEnd] = [
      {
        x: (posArr[(i - 1) * position.itemSize + 0] - gridLeft) / cellSize,
        y: (posArr[(i - 1) * position.itemSize + 1] - gridBottom) / cellSize,
      },
      {
        x: (posArr[i * position.itemSize + 0] - gridLeft) / cellSize,
        y: (posArr[i * position.itemSize + 1] - gridBottom) / cellSize,
      },
    ]

    // const startIdx = { x: Math.trunc(lineStart.x), y: Math.trunc(lineStart.y) }
    // const endIdx = { x: Math.trunc(lineEnd.x), y: Math.trunc(lineEnd.y) }
    // cellIndexMap[`${startIdx.x}-${startIdx.y}`] = true
    // cellIndexMap[`${endIdx.x}-${endIdx.y}`] = true

    const slope = (lineEnd.y - lineStart.y) / (lineEnd.x - lineStart.x)
    const gridXSpans = getIntArrBetweenRange(lineStart.x, lineEnd.x)
    gridXSpans.forEach((x) => {
      const y = (x - lineStart.x) * slope + lineStart.y
      const idx = { x: Math.trunc(x), y: Math.trunc(y) }
      cellIndexMap[`${idx.x}-${idx.y}`] = true
      cellIndexMap[`${idx.x - 1}-${idx.y}`] = true
    })
    const gridYSpans = getIntArrBetweenRange(lineStart.y, lineEnd.y)
    gridYSpans.forEach((y) => {
      const x = (y - lineStart.y) / slope + lineStart.x
      const idx = { x: Math.trunc(x), y: Math.trunc(y) }
      cellIndexMap[`${idx.x}-${idx.y}`] = true
      cellIndexMap[`${idx.x}-${idx.y - 1}`] = true
    })
  }
  return cellIndexMap
}

const cellArray = ref([])
function showCell(ctx, gridConfig, cellIndexMap) {
  cellArray.value = Object.keys(cellIndexMap).map((key) =>
    key.split("-").map((d) => +d)
  )

  const [gridLeft, gridBottom] = [
    gridConfig.center.x - gridConfig.size / 2,
    gridConfig.center.y - gridConfig.size / 2,
  ]
  const cellSize = gridConfig.size / gridConfig.divisions
  ctx.cells && ctx.scene.remove(ctx.cells)
  ctx.cells = TT.addInstanceMesh(
    ctx,
    cellArray.value.map(([i, j]) => [
      (i + 0.5) * cellSize + gridLeft,
      (j + 0.5) * cellSize + gridBottom,
      0,
    ]),
    "cell",
    { size: [cellSize, cellSize, 0.1] }
  )
}
</script>

<template>
  <div class="stl-loader">
    <div class="ws-recv-msg">Gen2DViewDemo's websocket message: {{wsRecvMsg}}</div>
    <el-row justify="space-between" style="height: 400px">
      <el-col :span="8">
        <div ref="container" class="container full-w" style="height: 100%;" />
      </el-col>
      <el-col :span="8">
        <el-button type="primary" @click="onGen2DViewClick" style="margin-top: 20px;">Gen 2D View</el-button>
        <div ref="containerWire" class="container full-w" style="width: 80%; height: 80%; margin: 20px 10%;" />
      </el-col>
      <el-col :span="8">
        <div ref="container2d" class="container full-w" style="height: 100%;" />
      </el-col>
    </el-row>

    <div v-if="cellArray.length" class="cell-group">
      {{cellArray}}
    </div>
    <!-- <div class="cell-group">
      <div v-for="j in gridDivisions" :key="j" class="flex-row" style="justify-content: center;">
        <div class="cell" v-for="i in gridDivisions" :key="i" :style="{background: cellIndexMap[`${i - 1}-${gridDivisions - j}`] ? 'red' : 'none'}" />
      </div>
    </div> -->
  </div>
</template>

<style lang="scss" scoped>
.ws-recv-msg {
  background: #444;
  margin-bottom: 20px;
  text-align: start;
  padding: 10px;
}
.container {
  position: relative;
  margin-top: 20px;
}
.cell-group {
  margin-top: 40px;
  text-align: start;
  .cell {
    text-align: center;
    display: inline-block;
    width: 40px;
    height: 40px;
    border: 1px solid;
  }
}
</style>

